import os
import bz2
import pytz
import struct
import logging
import logging.handlers
from typing import Any, Union, Dict
from datetime import datetime

# __all__ = ["Util"]
# warnings.simplefilter(action='ignore')
_LOGGERS = {}


class Util(object):
    tz: Any = pytz.timezone("Asia/Shanghai")
    log_levels: Dict[str, Any] = {
        'debug': logging.DEBUG,
        'info': logging.INFO,
        'warnging': logging.WARNING,
        'error': logging.ERROR,
        'critical': logging.CRITICAL
    }

    @classmethod
    def pyfile_logger(
        cls,
        pyfile_name: str,
        level: str="info",
        log_path: Union[None, str]=None
    ) -> logging.Logger:
        """
        1.为python文件创建和配置记录器
        2.日志文件将存储在同一目录中的“log”目录中
        3.目录使用python文件名称
        4.引发异常：如果无法创建或访问日志目录。
        ：param pyfile_name: 要为其创建记录器的python文件的名称
        ：param level: 记录器设置的日志记录级别, 默认值为“info”
        ：param log_path: 日志文件应存储的路径, 可为空
        ：return: 已配置的日志记录, 记录器对象。
        """
        if log_path:
            dirname = log_path
        else:
            perfix = os.path.dirname(pyfile_name)
            dirname = os.path.join(perfix, "log")
        basename = os.path.basename(pyfile_name).replace(".py", "")
        filename = os.path.join(dirname, basename)

        # 检查文件夹是否存在，如果不存在，则创建文件夹
        if not os.path.exists(filename):
            os.makedirs(filename)
        return cls.init_log(basename, level, filename)

    @classmethod
    def init_log(
        cls,
        tag: str,
        level: str,
        filename: Union[str, None]=None,
        backup_count: int=3
    ) -> logging.Logger:
        """
        1.初始化记录器
        2.创建日志处理器
        ：param标签：记录器的唯一标识符
        ：param level：为记录器设置的日志记录级别, 选项包括“调试”、“信息”、“警告”、“错误”和“严重”
        ：param文件名：可选；日志文件的基本名称。如果提供, 日志将写入文件。如果没有，日志将输出到控制台
        ：param backup_count：可选 要保留的备份日志文件的数量，默认三天的数据, 仅在提供“filename”时适用
        ：return：记录器实例
        """
        # 缓存记录器实例
        if _LOGGERS.get(tag):
            return _LOGGERS.get(tag)

        outputs = [
            "[%(asctime)s | ",
            "%(filename)s, ",
            "%(funcName)s, ",
            "line %(lineno)d | ",
            "%(levelname)s] ",
            "%(message)s",
        ]
        formatter = logging.Formatter("".join(outputs))
        logger = logging.getLogger(tag)
        logger.setLevel(cls.log_levels[level])

        if filename:
            name = os.path.join(filename, f'{tag}.log')
            hand = logging.handlers.TimedRotatingFileHandler(
                name,
                when='midnight',
                backupCount=backup_count
            )
        else:
            hand = logging.StreamHandler()

        hand.setLevel(cls.log_levels["debug"])
        hand.setFormatter(formatter)
        logger.addHandler(hand)
        _LOGGERS.update({tag: logger})

        return logger

    @classmethod
    async def timestamp2datetime(cls, ts: int, utc: bool = False) -> datetime:
        """
        ts:1546272000
        Util.timestamp2datetime(ts)
            datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
        """
        dt = datetime.fromtimestamp(int(ts), cls.tz)
        return cls.datetime2utcdatetime(dt) if utc else dt

    @staticmethod
    async def datetime2utcdatetime(dt: datetime) -> datetime:
        """
        dt: datetime.datetime(2019, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
        Util.datetime2utcdatetime(dt): datetime.datetime(2018, 12, 31, 16, 0, tzinfo=<UTC>)
        """
        return dt.astimezone(pytz.utc)

    @staticmethod
    async def str_to_timestamp(time_str: str):
        # 解析日期字符串为 datetime 对象
        dt = datetime.fromisoformat(time_str)
        timestamp = dt.timestamp() * 1000
        return int(timestamp)

    @staticmethod
    async def format_data(res, decoded_data):
        """
        解析数据：处理 res 中的 Content 数据转为 list 格式
        :param res: 原始数据
        :param decoded_data: 待处理数据
        :return: 列表数据
        """
        # 处理解压
        if (res.__contains__("ZipType")):
            if res['ZipType'] == 1:
                try:
                    decoded_data = bz2.decompress(bytes(decoded_data))
                except Exception as e:
                    print(f":ZipType: {res['ZipType']}, Code: {res['Code']}, Errer:{e}")

        length = len(decoded_data)
        short_array = []
        # 解析数据
        for index, val in enumerate(decoded_data):
            if (index * 2 >= length): break
            short_value = struct.unpack('>h', bytes(decoded_data[2 * index:2 * index + 2]))
            short_array.append(short_value[0])
        original_values_list = [i * res['Coef'] for i in short_array]
        return original_values_list

